import numpy as np

'''
多元线性回归 测试数据1
  X1  X2  X3  X4  Y
[5.1 3.5 1.4 0.2] 0
[4.8 3.4 1.9 0.2] 0
[6.9 3.1 4.9 1.5] 1
[5.9 3.2 4.8 1.8] 1
[6.5 3.2 5.1 2. ] 2
[7.2 3.2 6.  1.8] 2
多元线性回归 测试数据2
 X1	  X2  X3	 Y
[2.5 3.1 4.0]   19.0
[1.2 0.9 3.2]	8.6
[4.8 2.7 1.6]	22.4
[2.9 3.8 2.2]	18.2
[4.2 1.1 3.5]	21.0
[1.8 2.3 1.4]	10.2
[3.7 4.0 3.1]	24.6
[0.9 0.6 2.0]	5.4
'''

class MutiplyRegress:
    def __init__(self, remain_dec=2):
        # 斜率
        #FIXME: 该参数是一个向量，因为是多元数据，所以斜率也为多个
        self.slope = np.array([])
        # 截距
        self.intercept = 0.0
        # remain_dec模型默认保留小数位数
        self.remain_dec = remain_dec
        # 过滤列属性
        self.__filter_x = np.array([])

    # 检测矩阵行向量是否为全一向量倍数
    def __isHorizontal(self,l:list[float])->bool:
        for i in range(1,len(l)):
            if l[i]!=l[i-1]:
                return False
        return True

    # 求矩阵data转置矩阵
    def transformMatrix(self, data: list[list[float]]):
        T, size = [], len(data)
        for i in range(len(data[0])):
            arr = []
            for j in range(size):
                arr.append(data[j][i])
            T.append(arr)
        return T

    # 数据预处理（对非满秩矩阵处理，使矩阵可逆）
    def preprocessData(self,data:list[list[float]]):
        data = self.transformMatrix(data)
        filterIndexs,res = [],[]
        for i,line in enumerate(data):
            if not self.__isHorizontal(line):
               filterIndexs.append(i)
               res.append(line)
        return self.transformMatrix(res),np.array(filterIndexs)


    def fit(self, train_x: list[list[float]], train_y: list[float]):
        # 预处理数据，避免矩阵不可逆
        train_x,self.__filter_x = self.preprocessData(train_x)
        X = [[*i, 1] for i in train_x]
        XT = self.transformMatrix(X)
        # XT与X矩阵积的逆矩阵
        XTX_1 = np.linalg.inv(np.dot(XT, X))
        res = np.dot(np.dot(XTX_1, XT), train_y)
        # 生成斜率
        self.slope = np.array([round(i,self.remain_dec) for i in res[:res.size-1]])
        # 生成截距
        self.intercept = round(res[res.size-1],self.remain_dec)

    # 预测结果
    def predict(self,test_x: list[float]):
        text_x = [val for i,val in enumerate(test_x) if np.isin(i,self.__filter_x)]
        res = self.intercept
        for index,k in enumerate(self.slope):
            res+=(k*text_x[index])
        return round(res,self.remain_dec)

    def predicts(self,test_y:list[list[float]]):
        res = []
        for i in test_y:
            res.append(self.predict(i))
        return res